#pragma once
//
//	FPackedNormal
//

#include "vector3.h"
#include "vector4.h"
#include "mathlib.h"
#include "matrix4.h"
#include "colorvalue.h"
#include "AxisAlignedBox.h"

namespace RenderSystem
{
	struct FPackedNormal
	{
		union
		{
			struct
			{
				BYTE X,Y,Z,W;
			};
			DWORD		Packed;
		}				Vector;

		// Constructors.

		FPackedNormal( ) { Vector.Packed = 0; }
		FPackedNormal( DWORD InPacked ) { Vector.Packed = InPacked; }
		FPackedNormal( const Common::Vector3& InVector ) { *this = InVector; }

		// Conversion operators.

		void operator=(const Common::Vector3& InVector);
		void operator=(const Common::Vector4& InVector);
		
		operator Common::Vector3( ) const;
		//operator VectorRegister( ) const;

		// Set functions.
		void Set( const Common::Vector3& InVector ) { *this = InVector; }

		// Equality operator.

		bool operator==(const FPackedNormal& B) const;

	};

	/** X=127.5, Y=128.0, Z=1/127.5f, W=-1.0 */
	//extern const VectorRegister GVectorPackingConstants;

	/**
	* FPackedNormal operator =
	*/
	FORCEINLINE void FPackedNormal::operator=(const Common::Vector3& InVector)
	{
		Vector.X = Common::Math::Clamp<float>((InVector.x * 127.5f + 128.0f), 0, 255);
		Vector.Y = Common::Math::Clamp<float>((InVector.y * 127.5f + 128.0f), 0, 255);
		Vector.Z = Common::Math::Clamp<float>((InVector.z * 127.5f + 128.0f), 0, 255);
		Vector.W = 127.5f;
	}

	/**
	* FPackedNormal operator = for Common::Vector34. Only for PC. 
	* If you'd need this for Xbox360 or PS3 check operator=(Common::Vector3) 
	*/
	FORCEINLINE void FPackedNormal::operator=(const Common::Vector4& InVector)
	{
		Vector.X = Common::Math::Clamp<float>((InVector.x * 127.5f + 128.0f), 0, 255);
		Vector.Y = Common::Math::Clamp<float>((InVector.y * 127.5f + 128.0f), 0, 255);
		Vector.Z = Common::Math::Clamp<float>((InVector.z * 127.5f + 128.0f), 0, 255);
		Vector.W = Common::Math::Clamp<float>((InVector.w * 127.5f + 128.0f), 0, 255);
	}


	/**
	* FPackedNormal operator ==
	*/
	FORCEINLINE bool FPackedNormal::operator==(const FPackedNormal& B) const
	{
		if(Vector.Packed != B.Vector.Packed)
			return 0;

		Common::Vector3	V1 = *this;
		Common::Vector3	V2 = B;

		if(::fabs(V1.x - V2.x) > 0.1f)
			return 0;

		if(::fabs(V1.y - V2.y) > 0.1f)
			return 0;

		if(::fabs(V1.z - V2.z) > 0.1f)
			return 0;

		return 1;
	}

	/**
	* Constructs a basis matrix for the axis vectors and returns the sign of the determinant
	*
	* @param XAxis - x axis (tangent)
	* @param YAxis - y axis (binormal)
	* @param ZAxis - z axis (normal)
	* @return sign of determinant either -1 or +1 
	*/
	/*FORCEINLINE FLOAT GetBasisDeterminantSign( const Common::Vector3& XAxis, const Common::Vector3& YAxis, const Common::Vector3& ZAxis )
	{
		Common::Matrix4 Basis(
			FPlane(XAxis,0),
			FPlane(YAxis,0),
			FPlane(ZAxis,0),
			FPlane(0,0,0,1)
			);
		return (Basis.Determinant() < 0) ? -1.0f : +1.0f;
	}*/

	/**
	* Constructs a basis matrix for the axis vectors and returns the sign of the determinant
	*
	* @param XAxis - x axis (tangent)
	* @param YAxis - y axis (binormal)
	* @param ZAxis - z axis (normal)
	* @return sign of determinant either 0 (-1) or +1 (255)
	*/
	/*FORCEINLINE BYTE GetBasisDeterminantSignByte( const FPackedNormal& XAxis, const FPackedNormal& YAxis, const FPackedNormal& ZAxis )
	{
		return appTrunc(GetBasisDeterminantSign(XAxis,YAxis,ZAxis) * 127.5f + 127.5f);
	}*/

	//	FPixelFormatInfo
	//

	struct FPixelFormatInfo
	{
		const TCHAR*	Name;
		INT				BlockSizeX,
			BlockSizeY,
			BlockSizeZ,
			BlockBytes,
			NumComponents;
		/** Platform specific token, e.g. D3DFORMAT with D3DDrv										*/
		DWORD			PlatformFormat;
		/** Format specific internal flags, e.g. whether SRGB is supported with this format		*/
		DWORD			Flags;
		BOOL			bSupported;
	};

	extern FPixelFormatInfo GPixelFormats[];		// Maps members of EPixelFormat to a FPixelFormatInfo describing the format.

	//
	//	CalculateImageBytes
	//

	extern SIZE_T CalculateImageBytes(DWORD SizeX,DWORD SizeY,DWORD SizeZ,BYTE Format);

	/**
	* Handles initialization/release for a global resource.
	*/
	template<class ResourceType>
	class TGlobalResource : public ResourceType
	{
	public:
		TGlobalResource()
		{
			if(IsInRenderingThread())
			{
				// If the resource is constructed in the rendering thread, directly initialize it.
				((ResourceType*)this)->InitResource();
			}
			else
			{
				// If the resource is constructed outside of the rendering thread, enqueue a command to initialize it.
				BeginInitResource((ResourceType*)this);
			}
		}

		virtual ~TGlobalResource()
		{
			// This should be called in the rendering thread, or at shutdown when the rendering thread has exited.
			// However, it may also be called at shutdown after an error, when the rendering thread is still running.
			// To avoid a second error in that case we don't assert.

			// Cleanup the resource.
			((ResourceType*)this)->ReleaseResource();
		}
	};

	/** A global white texture. */
	extern class FTexture* GWhiteTexture;

	/** A global white cube texture. */
	extern class FTexture* GWhiteTextureCube;

	//
	// Primitive drawing utility functions.
	//

	// Solid shape drawing utility functions. Not really designed for speed - more for debugging.
	// These utilities functions are implemented in UnScene.cpp using GetTRI.

	extern void DrawBox(class FPrimitiveDrawInterface* PDI,const Common::Matrix4& BoxToWorld,const Common::Vector3& Radii,const MaterialInstance* MaterialRenderProxy,BYTE DepthPriority);
	extern void DrawSphere(class FPrimitiveDrawInterface* PDI,const Common::Vector3& Center,const Common::Vector3& Radii,INT NumSides,INT NumRings,const MaterialInstance* MaterialRenderProxy,BYTE DepthPriority);
	extern void DrawCone(class FPrimitiveDrawInterface* PDI,const Common::Matrix4& ConeToWorld, FLOAT Angle1, FLOAT Angle2, INT NumSides, bool bDrawSideLines, const FColor& SideLineColor, const MaterialInstance* MaterialRenderProxy, BYTE DepthPriority);


	extern void DrawCylinder(class FPrimitiveDrawInterface* PDI,const Common::Vector3& Base, const Common::Vector3& XAxis, const Common::Vector3& YAxis, const Common::Vector3& ZAxis,
		FLOAT Radius, FLOAT HalfHeight, INT Sides, const MaterialInstance* MaterialInstance, BYTE DepthPriority);
	extern void DrawChoppedCone(class FPrimitiveDrawInterface* PDI,const Common::Vector3& Base, const Common::Vector3& XAxis, const Common::Vector3& YAxis, const Common::Vector3& ZAxis,
		FLOAT Radius, FLOAT TopRadius, FLOAT HalfHeight, INT Sides, const MaterialInstance* MaterialInstance, BYTE DepthPriority);


	// Line drawing utility functions.

	extern void DrawWireBox(class FPrimitiveDrawInterface* PDI,const Common::AxisAlignedBox& Box,FColor Color,BYTE DepthPriority);
	extern void DrawCircle(class FPrimitiveDrawInterface* PDI,const Common::Vector3& Base,const Common::Vector3& X,const Common::Vector3& Y,FColor Color,FLOAT Radius,INT NumSides,BYTE DepthPriority);
	extern void DrawWireSphere(class FPrimitiveDrawInterface* PDI, const Common::Vector3& Base, FColor Color, FLOAT Radius, INT NumSides, BYTE DepthPriority);
	extern void DrawWireCylinder(class FPrimitiveDrawInterface* PDI,const Common::Vector3& Base,const Common::Vector3& X,const Common::Vector3& Y,const Common::Vector3& Z,FColor Color,FLOAT Radius,FLOAT HalfHeight,INT NumSides,BYTE DepthPriority);
	extern void DrawWireChoppedCone(class FPrimitiveDrawInterface* PDI,const Common::Vector3& Base,const Common::Vector3& X,const Common::Vector3& Y,const Common::Vector3& Z,FColor Color,FLOAT Radius,FLOAT TopRadius,FLOAT HalfHeight,INT NumSides,BYTE DepthPriority);
	extern void DrawOrientedWireBox(FPrimitiveDrawInterface* PDI,const Common::Vector3& Base,const Common::Vector3& X,const Common::Vector3& Y,const Common::Vector3& Z, Common::Vector3 Extent, FColor Color,BYTE DepthPriority);
	extern void DrawDirectionalArrow(class FPrimitiveDrawInterface* PDI,const Common::Matrix4& ArrowToWorld,FColor InColor,FLOAT Length,FLOAT ArrowSize,BYTE DepthPriority);
	extern void DrawWireStar(class FPrimitiveDrawInterface* PDI,const Common::Vector3& Position, FLOAT Size, FColor Color,BYTE DepthPriority);
	extern void DrawDashedLine(class FPrimitiveDrawInterface* PDI,const Common::Vector3& Start, const Common::Vector3& End, FColor Color, FLOAT DashSize,BYTE DepthPriority);
	extern void DrawWireDiamond(class FPrimitiveDrawInterface* PDI,const Common::Matrix4& DiamondMatrix, FLOAT Size, const FColor& InColor,BYTE DepthPriority);

	/**
	* Draws a wireframe of the bounds of a frustum as defined by a transform from clip-space into world-space.
	* @param PDI - The interface to draw the wireframe.
	* @param FrustumToWorld - A transform from clip-space to world-space that defines the frustum.
	* @param Color - The color to draw the wireframe with.
	* @param DepthPriority - The depth priority group to draw the wireframe with.
	*/
	extern void DrawFrustumWireframe(
		FPrimitiveDrawInterface* PDI,
		const Common::Matrix4& WorldToFrustum,
		FColor Color,
		BYTE DepthPriority
		);

	/** The indices for drawing a cube. */
	//extern const WORD GCubeIndices[12*3];

	/**
	* Maps from an X,Y,Z cube vertex coordinate to the corresponding vertex index.
	*/
	//inline WORD GetCubeVertexIndex(bool X, bool Y, bool Z) { return X * 4 + Y * 2 + Z; }

	/**
	* Draws a mesh, modifying the material which is used depending on the view's show flags.
	* Meshes with materials irrelevant to the pass which the mesh is being drawn for may be entirely ignored.
	*
	* @param PDI - The primitive draw interface to draw the mesh on.
	* @param Mesh - The mesh to draw.
	* @param WireframeColor - The color which is used when rendering the mesh with SHOW_Wireframe.
	* @param LevelColor - The color which is used when rendering the mesh with SHOW_LevelColoration.
	* @param PropertyColor - The color to use when rendering the mesh with SHOW_PropertyColoration.
	* @param PrimitiveInfo - The FScene information about the UPrimitiveComponent.
	* @param bSelected - True if the primitive is selected.
	* @param ExtraDrawFlags - optional flags to override the view family show flags when rendering
	* @return Number of passes rendered for the mesh
	*/
	/*extern INT DrawRichMesh(
		FPrimitiveDrawInterface* PDI,
		const struct FMeshElement& Mesh,
		const Common::ColourValue& WireframeColor,
		const Common::ColourValue& LevelColor,
		const Common::ColourValue& PropertyColor,
	class FPrimitiveSceneInfo *PrimitiveInfo,
		bool bSelected,
		DWORD ExtraDrawFlags=0
		);*/

	/**
	* A 3x1 of xyz(11:11:10) format.
	*/
	struct FPackedPosition
	{
		union
		{
			struct
			{
				INT	X :	11;
				INT	Y : 11;
				INT	Z : 10;
			} Vector;

			UINT		Packed;
		};

		// Constructors.
		FPackedPosition() : Packed(0) {}
		FPackedPosition(const Common::Vector3& Other) : Packed(0) 
		{
			Set(Other);
		}

		// Conversion operators.
		FPackedPosition& operator=( Common::Vector3 Other )
		{
			Set( Other );
			return *this;
		}

		operator Common::Vector3() const;
		operator VectorRegister() const;

		// Set functions.
		void Set( const Common::Vector3& InVector );
	};
}